#!/bin/bash

# ==============================================================================
# SIONlib 自动化构建与 RPM 打包脚本
# (适用于 openEuler 上的普通用户，如 sjk)
# ==============================================================================

# 配置参数默认值
LOCAL_INSTALL_PREFIX="/home/sjk/sionlib_local_install" # SIONlib 本地安装路径（用于测试）
RPM_VERSION="1.7.6"               # SIONlib 版本
SOURCE_URL="https://gitee.com/Lss__sjk/sionlib.git" # SIONlib 源码URL
RPM_SPEC_FILENAME="sionlib.spec"  # RPM Spec 文件名

# 日志文件
LOG_FILE="./build_sionlib_$(date +%Y%m%d%H%M%S).log"

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No Color

# 记录日志函数
log_info() {
    echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "${LOG_FILE}"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "${LOG_FILE}"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "${LOG_FILE}"
    exit 1
}

# 检查命令是否存在
check_cmd() {
    command -v "$1" >/dev/null 2>&1 || log_error "$1 command not found. Please install it (e.g., sudo dnf install $1)."
}

# 依赖检查与安装
install_dependencies() {
    log_info "Checking and installing dependencies..."
    check_cmd dnf
    check_cmd git
    check_cmd make
    check_cmd gcc
    check_cmd gfortran
    check_cmd mpicc
    check_cmd rpmbuild
    check_cmd rpmdev-setuptree

    if ! command -v rpmdev-setuptree >/dev/null 2>&1; then
        log_info "rpmdevtools not found, installing..."
        sudo dnf install rpmdevtools -y || log_error "Failed to install rpmdevtools."
    fi

    log_info "Ensuring BuildRequires packages are installed..."
    sudo dnf install gcc gcc-c++ gcc-gfortran openmpi-devel pkgconfig -y || log_error "Failed to install BuildRequires packages."

    log_info "All dependencies checked and installed."
}

# 源码下载
download_source() {
    log_info "Downloading SIONlib source from ${SOURCE_URL}..."
    if [ -d "sionlib" ]; then
        log_warn "Existing 'sionlib' directory found. Removing it."
        rm -rf sionlib || log_error "Failed to remove existing 'sionlib' directory."
    fi
    git clone "${SOURCE_URL}" sionlib || log_error "Failed to clone SIONlib repository."
    log_info "SIONlib source downloaded."
}

# 配置、编译和本地安装 SIONlib
build_local_sionlib() {
    log_info "Changing to sionlib source directory: $(pwd)/sionlib"
    pushd sionlib || log_error "Failed to change to sionlib source directory."

    log_info "Configuring SIONlib for local build..."
    ./configure --prefix="${LOCAL_INSTALL_PREFIX}" --compiler=gnu --mpi=openmpi --disable-parutils --force-64 || log_error "SIONlib local configure failed."

    # --- NEW STEP: Perform an initial 'make' to create build directories ---
    # This will create directories like build-linux-gomp-openmpi-64/build/fortraninterface
    log_info "Performing initial make to create build directories..."
    make -j$(nproc) || log_warn "Initial make failed (expected due to unpatched Fortran, but directories should be created)."
    # --- END NEW STEP ---

    # --- START: Apply Fortran compatibility patches to generated Makefiles (for local build) ---
    BUILD_SUBDIR=$(ls -d build-*)
    
    log_info "Applying Fortran patches to local build Makefiles..."

    # Escaping for $(VAR) in sed for shell execution: \$(VAR)
    pushd "${BUILD_SUBDIR}/build/fortraninterface"
    # More robust sed patterns to target any Fortran compiler command ending with -c
    # This pattern captures the compiler variable and all its flags/args up to '-c'
    # and inserts '-fallow-argument-mismatch' before '-c'.
    # This should cover F77, F90, MPIF77, MPIF90, OMPF77, OMPF90.
    
    # Pattern explanation:
    # (\$(F77)[^\\]*-c)      -> captures $(F77)...-c
    # ([^ ]*)             -> captures any characters (flags, args) until the next part
    # \s+                  -> captures one or more spaces
    # \1 -fallow-argument-mismatch -> replaces with captured group 1 and inserts the flag
    
    # Generic Fortran 77 files (.f)
    sed -i 's|\(\$\(F77\)[^[:blank:]]*-c\)|\1 -fallow-argument-mismatch|g' Makefile
    sed -i 's|\(\$\(MPIF77\)[^[:blank:]]*-c\)|\1 -fallow-argument-mismatch|g' Makefile
    sed -i 's|\(\$\(OMPF77\)[^[:blank:]]*-c\)|\1 -fallow-argument-mismatch|g' Makefile

    # Generic Fortran 90 files (.f90, .F90)
    sed -i 's|\(\$\(F90\)[^[:blank:]]*-c\)|\1 -fallow-argument-mismatch|g' Makefile
    sed -i 's|\(\$\(MPIF90\)[^[:blank:]]*-c\)|\1 -fallow-argument-mismatch|g' Makefile
    sed -i 's|\(\$\(OMPF90\)[^[:blank:]]*-c\)|\1 -fallow-argument-mismatch|g' Makefile
    popd

    # Modify parutils Makefile (if it has Fortran compilation rules)
    pushd "${BUILD_SUBDIR}/build/parutils"
    sed -i 's|\(\$\(MPIF90\)[^[:blank:]]*-c\)|\1 -fallow-argument-mismatch|g' Makefile
    popd

    # Disable Python tests for local build
    pushd "${BUILD_SUBDIR}/build/test/serial"
    sed -i '/TESTSPY = test_pysopn_1.py/cTESTSPY =' Makefile
    popd
    log_info "Patches applied to local build Makefiles."
    # --- END: Apply Fortran compatibility patches ---

    # --- NEW STEP: Clean after patching, before final compilation ---
    log_info "Cleaning build artifacts after patching..."
    make clean || log_error "make clean failed after patching."
    # --- END NEW STEP ---

    log_info "Compiling SIONlib locally (final build)..."
    make -j$(nproc) || log_error "SIONlib local compilation failed."

    log_info "Installing SIONlib to ${LOCAL_INSTALL_PREFIX}..."
    make install || log_error "SIONlib local installation failed."
    log_info "SIONlib built and installed locally successfully."

    popd # Return from sionlib dir
}



# 运行本地测试
run_local_tests() {
    log_info "Changing to sionlib source directory: $(pwd)/sionlib"
    pushd sionlib || log_error "Failed to change to sionlib source directory."

    log_info "Running SIONlib local tests..."
    pushd build-linux-gomp-openmpi-64/build || log_error "Failed to enter local build directory for testing."
    make test || log_error "SIONlib local tests failed."
    popd > /dev/null
    log_info "SIONlib local tests passed successfully."

    popd # Return from sionlib dir
}

# --- GLOBAL RPM SPEC TEMPLATE DEFINITION ---
# This block defines the multi-line RPM spec file content.
# The 'EOF_SPEC_TEMPLATE' delimiter MUST be flush left (no leading whitespace).
# All shell variables ($) and command substitutions ($(cmd)) inside the template
# must be escaped if you want them to be interpreted by RPM/bash *later*.
# Variables like ${RPM_VERSION} will be substituted by sed during build_rpm_packages.
read -r -d '' RPM_SPEC_TEMPLATE << 'EOF_SPEC_TEMPLATE'
Name:           sionlib
Version:        #RPM_VERSION_PLACEHOLDER#
Release:        1%{?dist}
Summary:        High-performance parallel I/O library for scientific applications
%global debug_package %{nil}

License:        LGPLv3+
URL:            https://www.fz-juelich.de/jsc/sionlib
Source0:        %{name}-%{version}.tar.gz

BuildRequires:  gcc, gcc-c++, gcc-gfortran, make
BuildRequires:  openmpi-devel
BuildRequires:  pkgconfig

Requires:       openmpi

%description
SIONlib is a high-performance parallel I/O library developed at Forschungszentrum Jülich.
It is widely used in scientific computing for collecting and processing performance data.
This package provides the SIONlib library and its development files for openEuler.

%package devel
Summary:        Development files for SIONlib
Requires:       %{name} = %{version}-%{release}
Requires:       openmpi-devel
Requires:       pkgconfig

%description devel
The %{name}-devel package contains the header files and libraries needed
to develop applications that use SIONlib.

%prep
%setup -q -n sionlib-%{version}

%build
cd %{_builddir}/sionlib-%{version}

# Patching fortran Makefiles in RPM build environment
# This is a copy of the sed commands used in build_local_sionlib, ensure they are correct
sed -i 's|\$(F77) \$(F77FLAGS) -c|$(F77) $(F77FLAGS) -fallow-argument-mismatch -c|g' src/fortraninterface/Makefile
sed -i 's|\$(MPIF77) \$(F77FLAGS) -c|$(MPIF77) $(F77FLAGS) -fallow-argument-mismatch -c|g' src/fortraninterface/Makefile
sed -i 's|\$(OMPF77) \$(F77FLAGS) -c|$(OMPF77) $(F77FLAGS) -fallow-argument-mismatch -c|g' src/fortraninterface/Makefile
sed -i 's|\$(F90) -c|$(F90) $(F90FLAGS) -fallow-argument-mismatch -c|g' src/fortraninterface/Makefile
sed -i 's|\$(MPIF90) \$(FFLAGS) -c|$(MPIF90) $(FFLAGS) -fallow-argument-mismatch -c|g' src/fortraninterface/Makefile
sed -i 's|\$(OMPF90) \$(FFLAGS) -c|$(OMPF90) $(FFLAGS) -fallow-argument-mismatch -c|g' src/fortraninterface/Makefile
sed -i 's|\$(MPIF90) \$(FFLAGS) -c|$(MPIF90) $(FFLAGS) -fallow-argument-mismatch -c|g' src/parutils/Makefile

sed -i '/TESTSPY = test_pysopn_1.py/cTESTSPY =' test/serial/Makefile

./configure --prefix=%{_prefix} --compiler=gnu --mpi=openmpi --disable-parutils --force-64

%make_build

%install
rm -rf %{buildroot}
cd %{_builddir}/sionlib-%{version}

BUILD_SUBDIR=$(ls -d build-*)

mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_libdir}
mkdir -p %{buildroot}%{_includedir}
mkdir -p %{buildroot}%{_docdir}/%{name}
mkdir -p %{buildroot}%{_libdir}/pkgconfig
mkdir -p %{buildroot}%{_licensedir}/%{name}

install -m 644 LICENSE %{buildroot}%{_licensedir}/%{name}
install -m 644 README %{buildroot}%{_docdir}/%{name}

find "${BUILD_SUBDIR}/build/lib" -maxdepth 1 -type f -name "sion*.h" -exec install -m 644 '{}' %{buildroot}%{_includedir} \;
find "${BUILD_SUBDIR}/build/fortraninterface" -maxdepth 1 -type f -name "sion*.h" -exec install -m 644 '{}' %{buildroot}%{_includedir} \;
find "${BUILD_SUBDIR}/build/cppinterface" -maxdepth 1 -type f -name "sion*.h" -exec install -m 644 '{}' %{buildroot}%{_includedir} \;

find "${BUILD_SUBDIR}/build/lib" -maxdepth 1 -type f -name "liblsion*.a" -exec install -m 644 '{}' %{buildroot}%{_libdir} \;

if [ -f "${BUILD_SUBDIR}/build/utils/sioncat" ]; then
    install -m 755 "${BUILD_SUBDIR}/build/utils/sioncat" %{buildroot}%{_bindir}
fi
if [ -f "${BUILD_SUBDIR}/build/utils/siondefrag" ]; then
    install -m 755 "${BUILD_SUBDIR}/build/utils/siondefrag" %{buildroot}%{_bindir}
fi
if [ -f "${BUILD_SUBDIR}/build/utils/siondump" ]; then
    install -m 755 "${BUILD_SUBDIR}/build/utils/siondump" %{buildroot}%{_bindir}
fi
if [ -f "${BUILD_SUBDIR}/build/utils/sionsplit" ]; then
    install -m 755 "${BUILD_SUBDIR}/build/utils/sionsplit" %{buildroot}%{_bindir}
fi
if [ -f "${BUILD_SUBDIR}/build/utils/sionversion" ]; then
    install -m 755 "${BUILD_SUBDIR}/build/utils/sionversion" %{buildroot}%{_bindir}
fi

cat > %{buildroot}%{_libdir}/pkgconfig/sionlib.pc << 'EOFF'
prefix=%{_prefix}
exec_prefix=\${prefix}
libdir=\${exec_prefix}/lib64
includedir=\${prefix}/include

Name: sionlib
Description: SIONlib high-performance parallel I/O library (static)
Version: #RPM_VERSION_PLACEHOLDER#
Cflags: -I\${includedir}
Libs: -L\${libdir} -lcom_64 -lcom_64_lock_none -lcom_64_lock_pthreads -lsionser_64 -lrt -lm -L/usr/lib64/openmpi/lib -lmpi_mpifh -lmpi_usempif08 -lmpi_usempi_ignore_tkr -lmpi_mpicxx -lmpi -lpthread
EOFF

%files
%license %{_licensedir}/%{name}/LICENSE
%doc README
%{_libdir}/liblsion*.a
%{_bindir}/sioncat
%{_bindir}/siondefrag
%{_bindir}/siondump
%{_bindir}/sionsplit
%{_bindir}/sionversion

%files devel
%{_includedir}/sion*
%{_libdir}/pkgconfig/*.pc

%changelog
* Tue Aug 27 2024 sjk <1261700278@qq.com> - #RPM_VERSION_PLACEHOLDER#-1
- Initial RPM package for SIONlib.
EOF_SPEC_TEMPLATE
# --- END GLOBAL RPM SPEC TEMPLATE DEFINITION ---


# 构建 RPM 包
build_rpm_packages() {
    log_info "Preparing RPM build environment..."
    rpmdev-setuptree || log_error "Failed to setup rpmbuild tree. Make sure rpmdevtools is installed."

    log_info "Creating source tarball sionlib-${RPM_VERSION}.tar.gz..."
    # cd 到 sionlib 项目的目录，然后 git archive
    # pushd/popd 确保命令在正确的目录执行，并返回原目录
    (pushd "${SCRIPT_DIR}/sionlib" && git archive --format=tar.gz --prefix=sionlib-${RPM_VERSION}/ HEAD -o "${HOME}/rpmbuild/SOURCES/sionlib-${RPM_VERSION}.tar.gz" && popd) || log_error "Failed to create source tarball."

    log_info "Writing spec file to ${HOME}/rpmbuild/SPECS/${RPM_SPEC_FILENAME}..."
    # 对全局模板变量进行版本替换并写入 spec 文件
    # 使用printf而非echo，避免不必要的转义问题
    printf "%s" "${RPM_SPEC_TEMPLATE}" | sed -e "s|#RPM_VERSION_PLACEHOLDER#|${RPM_VERSION}|g" > "${HOME}/rpmbuild/SPECS/${RPM_SPEC_FILENAME}" || log_error "Failed to write spec file."

    log_info "Building SIONlib RPM packages..."
    rpmbuild -ba "${HOME}/rpmbuild/SPECS/${RPM_SPEC_FILENAME}" || log_error "RPM package build failed."
    log_info "RPM packages built successfully. Check ~/rpmbuild/RPMS and ~/rpmbuild/SRPMS."
}

# 显示帮助信息
show_help() {
    echo "Usage: $0 [OPTIONS]"
    echo "Automates SIONlib build and RPM packaging on openEuler."
    echo ""
    echo "Options:"
    echo "  -p, --prefix <path>      Local installation prefix for SIONlib (default: ${LOCAL_INSTALL_PREFIX})"
    echo "  -v, --version <ver>      SIONlib version (default: ${RPM_VERSION})"
    echo "  -h, --help               Show this help message"
    echo ""
    echo "This script should be run as a non-root user (e.g., 'sjk')."
    echo "It will clone the sionlib repository, apply patches, perform a local build & test,"
    echo "then build RPM packages. Make sure necessary build tools (gcc, make, openmpi-devel,"
    echo "rpm-build, rpmdevtools) are installed."
    exit 0
}

# 解析命令行参数
while [[ "$#" -gt 0 ]]; do
    case $1 in
        -p|--prefix) LOCAL_INSTALL_PREFIX="$2"; shift ;;
        -v|--version) RPM_VERSION="$2"; shift ;;
        -h|--help) show_help ;;
        *) log_error "Unknown parameter passed: $1. Use -h for help." ;;
    esac
    shift
done

# 获取脚本所在目录的绝对路径
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"

# 主函数调用
log_info "Starting SIONlib build and RPM packaging process..."
install_dependencies
download_source # Downloads into ./sionlib
build_local_sionlib # Operates in ./sionlib
modify_test_scripts # Operates in ./sionlib
run_local_tests # Operates in ./sionlib
build_rpm_packages # Operates from SCRIPT_DIR
log_info "SIONlib build and RPM packaging process completed successfully!"

exit 0
